package com.itheima.atte.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.atte.dto.AttendGroupDTO;
import com.itheima.atte.dto.AttendPunchDTO;
import com.itheima.atte.entity.AttendPunch;
import com.itheima.atte.enums.AttendEnums;
import com.itheima.atte.mapper.AttendPunchMapper;
import com.itheima.atte.service.IAttendGroupService;
import com.itheima.atte.service.IAttendPunchService;
import com.itheima.common.exception.NcException;
import com.itheima.common.exception.enums.ResponseEnum;
import com.itheima.common.threadlocal.UserHolder;
import com.itheima.common.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author itheima
 * @since 2020-12-21
 */
@Slf4j
@Service
public class AttendPunchServiceImpl extends ServiceImpl<AttendPunchMapper, AttendPunch> implements IAttendPunchService {

    @Autowired
    private IAttendGroupService attendGroupService;


    @Override
    public void punch(AttendPunchDTO attendPunchDTO) {
        Date now = new Date();
        AttendPunch attendPunch = BeanHelper.copyProperties(attendPunchDTO, AttendPunch.class);
        attendPunch.setPunchTime(now);
        attendPunch.setPunchDateStr(DateTimeUtil.dateToStr(now, "yyyy-MM-dd"));

        //获得打卡用户的考勤组信息
        AttendGroupDTO attendGroupDTO = attendGroupService.queryAttendGroupByCompanyUserId(UserHolder.getCompanyUserId());
        if (attendGroupDTO == null) {
            throw new NcException(ResponseEnum.USER_NOT_MATCH_ATTGROUP);
        }
        log.info("打卡用户：{}，匹配的考勤组为：{}", UserHolder.getUserInfo(), attendGroupDTO);
        //判断是否在有效区域范围打卡
        if (!validArea(attendPunch, attendGroupDTO, attendPunchDTO)) {
            throw new NcException(ResponseEnum.PUNCH_INVALID_AREA);
        }
        //判断是否为工作日打卡
        if (!validWorkDay(attendPunch, attendGroupDTO)) {
            throw new NcException(ResponseEnum.PUNCH_INVALID_DAY);
        }
        //计算上下午打开是否为有效打卡
        Integer punchOnOffWork = attendPunchDTO.getPunchOnOffWork();
        if (punchOnOffWork == AttendEnums.NOON_TYPE_MORNING.value()) {
            //上班打卡
            attendPunch.setNoonType(AttendEnums.NOON_TYPE_MORNING.value());
            this.validEffective(attendPunch, AttendEnums.NOON_TYPE_MORNING);
        } else if (punchOnOffWork == AttendEnums.NOON_TYPE_AFTERNOON.value()) {
            attendPunch.setNoonType(AttendEnums.NOON_TYPE_AFTERNOON.value());
            this.validEffective(attendPunch, AttendEnums.NOON_TYPE_AFTERNOON);
        }
        //判断打卡类型 1正常；2迟到；3早退；4旷工5非工作日打卡
        this.setPunchType(attendPunch, attendGroupDTO);

        attendPunch.setPunchSource(AttendEnums.PUNCH_NORMAL.value());

        attendPunch.setCompanyUserId(UserHolder.getCompanyUserId());
        attendPunch.setCompanyId(UserHolder.getCompanyId());
        boolean b = this.save(attendPunch);
        if (!b) {
            throw new NcException(ResponseEnum.ERROR);
        }
        log.info("用户{},打卡成功,打卡信息：{}", UserHolder.getUserInfo(), attendPunch.toString());
    }

    /**
     * 判断打卡类型， 1正常；2迟到；3早退；4旷工
     */
    private void setPunchType(AttendPunch attendPunch, AttendGroupDTO attendGroup) {

        Integer allowLateMinutes = attendGroup.getAllowLateMinutes();//允许迟到时间
        Integer lateMinutes = attendGroup.getLateMinutes();//迟到时间-旷工时间
        String punchTimeStr = DateTimeUtil.dateToStr(attendPunch.getPunchTime(), DateTimeUtil.TIME_FORMAT_1);//打卡时间字符串
        //初始化为正常打卡
        AttendEnums punchType = AttendEnums.PUNCH_TYPE_OK;

        if (attendPunch.getNoonType() == AttendEnums.NOON_TYPE_MORNING.value()) {//判断上午
            String startWorkTime = attendGroup.getStartWorkTime();
            //设置上午打卡类型
            if (DateUtil.compareTime(punchTimeStr, startWorkTime, allowLateMinutes) == 1) {
                punchType = AttendEnums.PUNCH_TYPE_LATE;
                if (DateUtil.compareTime(punchTimeStr, startWorkTime, lateMinutes) == 1) {
                    punchType = AttendEnums.PUNCH_TYPE_STAYALAY;
                }
            }
            attendPunch.setMorningPunchType(punchType.value());
        } else {//判断下午
            String offWorkTime = attendGroup.getOffWorkTime();
            //设置下午打卡类型
            if (DateUtil.compareTime(offWorkTime, punchTimeStr, allowLateMinutes) == 1) {
                punchType = AttendEnums.PUNCH_TYPE_EARLY;
                if (DateUtil.compareTime(offWorkTime, punchTimeStr, lateMinutes) == 1) {
                    punchType = AttendEnums.PUNCH_TYPE_STAYALAY;
                }
            }
            attendPunch.setAfternoonPunchType(punchType.value());
        }
    }

    /**
     * 判断是否在有效范围内打卡
     *
     * @param attendPunch
     * @param attendGroup
     * @param request
     * @return
     */
    private Boolean validArea(AttendPunch attendPunch, AttendGroupDTO attendGroup, AttendPunchDTO request) {
        //比较“打卡坐标”和“考勤组打卡坐标”的距离
        int distance = DistanceUtil.getDistanceMeter(request.getLat(), request.getLng(), attendGroup.getLat().doubleValue(), attendGroup.getLng().doubleValue());
        //比较距离和有效距离
        Boolean flag = distance <= attendGroup.getAddressRange();
        attendPunch.setAreaValid(flag);
        return flag;
    }

    /**
     * 判断是否为有效打卡
     */
    private void validEffective(AttendPunch attendPunch, AttendEnums attendEnums) {
        Long userId = attendPunch.getCompanyUserId();
        String dateStr = attendPunch.getPunchDateStr();
        Integer noonType = attendPunch.getNoonType();
        QueryWrapper<AttendPunch> qryWrapper = new QueryWrapper<AttendPunch>()
                .eq("user_id={0}", userId)
                .eq("punch_date_str={0}", dateStr)
                .eq("noon_type={0}", attendEnums.value())
                .eq("area_valid", 1)
                .eq("company_id", UserHolder.getCompanyId())
                .eq("day_type", 1);
        if (this.count(qryWrapper) != 0 && noonType == 2) {
            this.remove(qryWrapper);
            //无论上下午都是有效的
            attendPunch.setEffectiveValid(true);
        }
        if (this.count(qryWrapper) != 0 && noonType == 1) {
            attendPunch.setEffectiveValid(false);
        } else {
            attendPunch.setEffectiveValid(true);
        }
    }

    /**
     * 判断是否为工作日
     */
    private Boolean validWorkDay(AttendPunch attendPunch, AttendGroupDTO attendGroup) {
        Integer week = cn.hutool.core.date.DateUtil.thisDayOfWeek();//1表示周日，2表示周一 3表示周二
        Integer[] workDays = ConvertUtil.string2Integer(attendGroup.getWorkdays().split(","));//[1,1,1,1,1,0,0]
        if (week == 1) {
            week += 6;
        } else {
            week -= 1;
        }
        Boolean flag = workDays[week - 1] == 1;
        attendPunch.setDayType(flag ? AttendEnums.DAY_TYPE_WORKDAY.value() : AttendEnums.PUNCH_TYPE_NOT_WORK_DAY.value());
        return flag;
    }
}
